local THANKYOUPOPUP = require "screens/thankyoupopup"
local ITEMBOXOPENERPOPUP = require "screens/redux/itemboxopenerpopup"
-------------------------------------------------------------------------------
-- 私有皮肤 ID 对照表（可动态扩展）
-------------------------------------------------------------------------------
local PRIVATE_SKINS = {
    ["sw_shadow_crown_skin1"]      = 1,
    ["sw_shadow_crown_skin2"]      = 2,
    ["sw_pigbot_skin1"]            = 3,
    ["sw_pigbot_skin2"]            = 4,
    ["sw_pig_cannon_skin1"]        = 5,
    ["sw_pig_cannon_skin2"]        = 6,
    ["sw_shadowwoven_tower_skin1"] = 7,
}
-------------------------------------------------------------------------------
-- 私有皮肤 ID 皮肤名
-------------------------------------------------------------------------------
local PRIVATE_SKINS_NAME = {
    [1] = { code = "sw_shadow_crown_skin1", name = "更深暗影", },
    [2] = { code = "sw_shadow_crown_skin2", name = "终极棱镜", },
    [3] = { code = "sw_pigbot_skin1", name = "启迪猪猪", },
    [4] = { code = "sw_pigbot_skin2", name = "暗影猪猪", },
    [5] = { code = "sw_pig_cannon_skin1", name = "启迪猪猪炮", },
    [6] = { code = "sw_pig_cannon_skin2", name = "暗影猪猪炮", },
    [7] = { code = "sw_shadowwoven_tower_skin1", name = "我们是冠军", },
}
local function skin_ids_to_codes(ids)
    local codes = {}
    for _, id in ipairs(ids) do
        if PRIVATE_SKINS_NAME[id] and type(PRIVATE_SKINS_NAME[id].code) == "string" then
            table.insert(codes, PRIVATE_SKINS_NAME[id].code)
        elseif PRIVATE_SKINS_NAME[id] and type(PRIVATE_SKINS_NAME[id].code) == "table" then
            for _, code in ipairs(PRIVATE_SKINS_NAME[id].code) do
                table.insert(codes, code)
            end
        end
    end
    return codes
end
-------------------------------------------------------------------------------
-- 混淆工具：简单字节位移 + HEX 编码
-------------------------------------------------------------------------------
local OBFUSCATE_OFFSET = 13 -- 可以改成任意 1~255 的数

local function encrypt(str)
    local t = {}
    for i = 1, #str do
        local b = str:byte(i)
        local x = (b + OBFUSCATE_OFFSET) % 256
        t[#t + 1] = string.format("%02X", x)
    end
    return table.concat(t)
end

local function decrypt(hexstr)
    local t = {}
    for i = 1, #hexstr, 2 do
        local bytehex = hexstr:sub(i, i + 1)
        local x = tonumber(bytehex, 16)
        local b = (x - OBFUSCATE_OFFSET) % 256
        t[#t + 1] = string.char(b)
    end
    return table.concat(t)
end

-------------------------------------------------------------------------------
-- 持久化文件操作与缓存
-------------------------------------------------------------------------------
local PERSIST_KEY = "sw_auth"
local auth_caches = {} -- [userid] = { loaded=true, skins={...} }

-- 保存某玩家授权信息
local function SaveAuthFor(userid, skin_list)
    if not userid or type(skin_list) ~= "table" then return end
    local raw    = json.encode({ userid = userid, skins = skin_list })
    local cipher = encrypt(raw)
    TheSim:SetPersistentString(PERSIST_KEY, cipher, false, function(ok)
    end)
end

-- 异步加载玩家授权到缓存（本地使用）
local function EnsureAuthLoaded(userid, refresh)
    if not userid then return end
    local cache = auth_caches[userid]
    if cache and cache.loaded and not refresh then return end
    cache = { loaded = false, skins = {} }
    auth_caches[userid] = cache

    TheSim:GetPersistentString(PERSIST_KEY, function(ok, data)
        if not ok or not data or data == "" then return end

        local okstr, raw = pcall(decrypt, data)
        if not okstr or type(raw) ~= "string" then
            return
        end

        local info = json.decode(raw)
        if type(info) == "table" and info.userid == userid and type(info.skins) == "table" then
            for _, id in ipairs(info.skins) do
                cache.skins[id] = true
            end
            cache.loaded = true

            -- 客机自动上报给主机
            if ThePlayer and ThePlayer.userid == userid and (TheNet:IsDedicated() or TheNet:GetIsServer() or TheNet:GetIsClient()) then
                local info_str = json.encode(info)
                SendModRPCToServer(GetModRPC("sw", "sync_skin_data"), info_str)
            end
        end
    end)
end

-------------------------------------------------------------------------------
-- 主机端保存所有玩家授权（world范围）
-------------------------------------------------------------------------------
local function EnsureWorldSkinData()
    if not TheWorld.sw_skin_data then
        TheWorld.sw_skin_data = {}
    end
end

-------------------------------------------------------------------------------
-- RPC 同步：客机->主机（玩家加入时自动触发）
-------------------------------------------------------------------------------
local RPC_NAMESPACE = "sw"
local RPC_NAME = "sync_skin_data"

AddModRPCHandler(RPC_NAMESPACE, RPC_NAME, function(player, data_str)
    if not data_str or type(data_str) ~= "string" then return end
    local info = json.decode(data_str)
    if type(info) ~= "table" or type(info.userid) ~= "string" or type(info.skins) ~= "table" then return end

    EnsureWorldSkinData()
    TheWorld.sw_skin_data[info.userid] = info
end)

-------------------------------------------------------------------------------
-- 客机：检查是否拥有皮肤
-------------------------------------------------------------------------------
SwSkinCheckFn = function(_, skinname)
    local player = ThePlayer
    if not player then return false, 0 end
    local uid = player.userid
    EnsureAuthLoaded(uid)
    local id = PRIVATE_SKINS[skinname]
    local cache = auth_caches[uid]
    if id and cache and cache.skins[id] then
        return true, 0
    end
    return false, 0
end

-------------------------------------------------------------------------------
-- 主机：检查指定玩家是否拥有皮肤（主机判定使用服务器同步数据）
-------------------------------------------------------------------------------
SwSkinCheckClientFn = function(_, userid, skinname)
    EnsureWorldSkinData()
    local info = TheWorld.sw_skin_data and TheWorld.sw_skin_data[userid]
    if not info or type(info.skins) ~= "table" then return false end
    local id = PRIVATE_SKINS[skinname]
    for _, sid in ipairs(info.skins) do
        if id and sid == id then
            return true
        end
    end
    return false
end

-------------------------------------------------------------------------------
-- 玩家加载进世界后立刻同步数据
-------------------------------------------------------------------------------
local function DelayEnsureAndSync(inst)
    -- 只在客户端执行
    if TheNet:IsDedicated() then return end
    if not ThePlayer then
        -- 理论不会出现，但防御
        inst:DoTaskInTime(2, DelayEnsureAndSync)
        return
    end
    local userid = ThePlayer.userid
    if not userid or userid == "" then
        -- userid 未初始化，延时再试
        ThePlayer:DoTaskInTime(2, DelayEnsureAndSync)
        return
    end
    -- 此时userid肯定OK，执行本地加载与同步
    EnsureAuthLoaded(userid, true)
    -- 再主动同步一次
    TheSim:GetPersistentString(PERSIST_KEY, function(ok, data)
        if not ok or not data or data == "" then return end
        local okstr, raw = pcall(decrypt, data)
        if not okstr or type(raw) ~= "string" then return end
        local info = json.decode(raw)
        if type(info) == "table" and info.userid == userid and type(info.skins) == "table" then
            local info_str = json.encode(info)
            SendModRPCToServer(GetModRPC("sw", "sync_skin_data"), info_str)
        end
    end)
end

AddPlayerPostInit(function(inst)
    -- 只在客户端 ThePlayer 上执行，防止所有玩家都触发
    if not TheNet:IsDedicated() then
        -- 延迟启动
        inst:DoTaskInTime(2, DelayEnsureAndSync)
    end
    if not TheWorld.ismastersim then
        return
    end
end)

-------------------------------------------------------------------------------
-- world保存与加载钩子
-------------------------------------------------------------------------------
AddPrefabPostInit("world", function(inst)
    local oldOnSave = inst.OnSave
    inst.OnSave = function(inst, data)
        EnsureWorldSkinData()
        data.sw_skin_data = TheWorld.sw_skin_data
        if oldOnSave ~= nil then
            oldOnSave(inst, data)
        end
    end

    local oldOnLoad = inst.OnLoad
    inst.OnLoad = function(inst, data)
        EnsureWorldSkinData()
        if data and data.sw_skin_data then
            TheWorld.sw_skin_data = data.sw_skin_data
        end
        if oldOnLoad ~= nil then
            oldOnLoad(inst, data)
        end
    end
end)

-------------------------------------------------------------------------------
-- CDK解密函数
-------------------------------------------------------------------------------
-- base64 解码器
local function base64_decode(data)
    local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    data = string.gsub(data, '[^' .. b .. '=]', '')
    return (data:gsub('.', function(x)
        if x == '=' then return '' end
        local r, f = '', (b:find(x) - 1)
        for i = 6, 1, -1 do
            r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and '1' or '0')
        end
        return r
    end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
        if #x ~= 8 then return '' end
        local c = 0
        for i = 1, 8 do
            c = c + (x:sub(i, i) == '1' and 2 ^ (8 - i) or 0)
        end
        return string.char(c)
    end))
end

-- 解密函数（简单位移+校验位）
local function decrypt(cdk)
    local raw = base64_decode(cdk)
    local decrypted = {}
    for i = 1, #raw do
        local b = raw:byte(i)
        decrypted[#decrypted + 1] = string.char((b - 13) % 256)
    end
    return table.concat(decrypted)
end

-- 位图解码（兼容 Lua 5.1，无位运算符）
local function decode_bitmap(bitmap_bytes)
    local skins = {}
    for i = 1, #bitmap_bytes do
        local byte = bitmap_bytes:byte(i)
        for j = 0, 7 do
            local mask = math.floor(2 ^ j)
            if (byte % (mask * 2)) >= mask then
                table.insert(skins, (i - 1) * 8 + j + 1)
            end
        end
    end
    return skins
end

-------------------------------------------------------------------------------
-- CDK输入界面
-------------------------------------------------------------------------------
local Screen = require "widgets/screen"
local Widget = require "widgets/widget"
local Text = require "widgets/text"
local Button = require "widgets/button"
local Image = require "widgets/image"
local TextEdit = require "widgets/textedit"
local TEMPLATES = require "widgets/redux/templates"

------------------------------------------------------
-- 小弹窗（输入内容不能为空提示）
local SmallPopup = Class(Screen, function(self, msg)
    Screen._ctor(self, "SmallPopup")

    local root = self:AddChild(Widget("root"))
    root:SetHAnchor(ANCHOR_MIDDLE)
    root:SetVAnchor(ANCHOR_MIDDLE)
    root:SetScaleMode(SCALEMODE_PROPORTIONAL)
    root:SetPosition(0, 0) -- 居中

    -- 小背景
    local bg = root:AddChild(TEMPLATES.CurlyWindow(360, 120, nil, nil, nil, ""))
    bg:SetPosition(0, 0)

    -- 文本
    local tip = root:AddChild(Text(DEFAULTFONT, 32, msg or "未知错误"))
    tip:SetColour(1, 1, 1, 1)
    tip:SetPosition(0, 25)

    -- 关闭按钮
    local closebtn = root:AddChild(TEMPLATES.StandardButton(function()
        TheFrontEnd:PopScreen(self)
    end, "关闭"))
    closebtn:SetPosition(0, -32)
    closebtn:SetScale(0.5)
end)

function SmallPopup:OnControl(control, down)
    if SmallPopup._base.OnControl(self, control, down) then return true end
    if not down and control == CONTROL_CANCEL then
        TheFrontEnd:PopScreen(self)
        return true
    end
end

------------------------------------------------------副窗主窗分割线

local SWShopPopup = Class(Screen, function(self)
    Screen._ctor(self, "SWShopPopup")

    -- 主根节点
    local root = self:AddChild(Widget("ROOT"))
    root:SetHAnchor(ANCHOR_MIDDLE)
    root:SetVAnchor(ANCHOR_MIDDLE)
    root:SetScaleMode(SCALEMODE_PROPORTIONAL)

    -- 带美术边框的弹窗
    self.dialog = root:AddChild(TEMPLATES.CurlyWindow(480, 420, "光影小卖铺", nil, nil, ""))
    self.dialog:SetPosition(0, 0)

    -- 输入框美术底
    local edit_bg = root:AddChild(Image("images/textboxes.xml", "textbox_long.tex"))
    edit_bg:ScaleToSize(500, 60)
    edit_bg:SetPosition(0, 80)

    -- 输入框本体
    self.input = root:AddChild(TextEdit(CODEFONT, 26, "", UICOLOURS.BLACK)) -- 字体更细、更淡
    self.input:SetPosition(0, 80)
    self.input:SetRegionSize(450, 36)
    self.input:SetForceEdit(true)
    self.input:SetEditing(true)
    self.input:SetTextLengthLimit(32)
    self.input:SetHAlign(ANCHOR_MIDDLE)
    -- 提示语（利用TextEdit的SetTextPrompt）
    if self.input.SetTextPrompt then
        self.input:SetTextPrompt("在此输入cdk", UICOLOURS.BLACK)
    end

    -- 确认、关闭按钮
    self.confirmbtn = root:AddChild(TEMPLATES.StandardButton(function() self:DoConfirm() end, "兑换"))
    self.confirmbtn:SetPosition(-100, -140)
    self.confirmbtn:SetScale(0.5)

    self.closebtn = root:AddChild(TEMPLATES.StandardButton(function() TheFrontEnd:PopScreen(self) end, "关闭"))
    self.closebtn:SetPosition(100, -140)
    self.closebtn:SetScale(0.5)

    self.default_focus = self.input
end)

function SWShopPopup:DoConfirm()
    local str = self.input:GetString() or ""
    str = str:gsub("^%s+", ""):gsub("%s+$", "") -- 去首尾空格
    if str == "" then
        TheFrontEnd:PushScreen(SmallPopup("输入内容不能为空"))
        return
    else
        -- 解密CDK
        local ok, raw = pcall(decrypt, str)
        if not ok or not raw or type(raw) ~= "string" or #raw < 10 then
            TheFrontEnd:PushScreen(SmallPopup("解密失败，格式错误"))
            return
        end

        -- 解析CDK内容
        local payload = raw:sub(1, -2)
        local checksum = raw:byte(-1)

        -- 校验和计算
        local sum = 0
        for i = 1, #payload do
            sum = (sum + payload:byte(i)) % 256
        end

        if sum ~= checksum then
            TheFrontEnd:PushScreen(SmallPopup("校验失败，CDK 被修改或损坏"))
            return
        end

        local uid, bitmap = payload:match("^([^|]+)|(.+)$")
        if not uid or not bitmap then
            TheFrontEnd:PushScreen(SmallPopup("解密格式错误"))
            return
        end
        local userid = "KU_" .. uid
        local skins = decode_bitmap(bitmap)

        -- 解码位图（假设CDK格式为位图）
        if #skins == 0 then
            TheFrontEnd:PushScreen(SmallPopup("没有读取到皮肤"))
            return
        end

        if ThePlayer then
            if ThePlayer.userid ~= userid then
                TheFrontEnd:PushScreen(SmallPopup("当前玩家的userid与CDK不匹配"))
                return
            end
            EnsureAuthLoaded(userid)
            local cache = auth_caches[userid] or { skins = {} }
            -- 把已有皮肤id变成set
            local owned = {}
            for id, v in pairs(cache.skins or {}) do
                owned[id] = true
            end

            -- 统计CDK里的皮肤
            local cdk_ids = {} -- 皮肤id数组
            for _, id in ipairs(skins) do
                cdk_ids[id] = true
            end

            -- 做并集，统计新老皮肤
            local already = {}
            local to_add = {}
            for id in pairs(cdk_ids) do
                if owned[id] then
                    table.insert(already, id)
                else
                    table.insert(to_add, id)
                end
            end

            -- 合并后皮肤列表
            local merged = {}
            for id in pairs(owned) do
                merged[id] = true
            end
            for id in pairs(cdk_ids) do
                merged[id] = true
            end

            -- 生成合并列表
            local merged_list = {}
            for id in pairs(merged) do
                table.insert(merged_list, id)
            end
            table.sort(merged_list)

            -- 友好提示
            if #to_add == 0 then
                TheFrontEnd:PushScreen(SmallPopup("你已经拥有该CDK下的所有皮肤"))
                return
            else
                SaveAuthFor(userid, merged_list)
                ThePlayer:DoTaskInTime(1, DelayEnsureAndSync)
                TheFrontEnd:PushScreen(SmallPopup("皮肤解锁成功"))
                if #to_add == 1 then
                    local item_name = PRIVATE_SKINS_NAME[to_add[1]] and PRIVATE_SKINS_NAME[to_add[1]].code
                    if item_name and type(item_name) == "string" then
                        -- 弹出开礼物包裹界面
                        local items = {
                            { item = item_name, item_id = 0, gifttype = "DEFAULT", message = '感谢支持' }
                        }
                        TheFrontEnd:PushScreen(THANKYOUPOPUP(items))
                    elseif item_name and type(item_name) == "table" then
                        -- 弹出开礼物箱子界面
                        local options = {
                            allow_cancel = true,
                            box_build = "box_shop_plain",
                            message = "请收下你的礼包"
                        }
                        TheFrontEnd:PushScreen(ITEMBOXOPENERPOPUP(options, function(success_cb) success_cb(item_name) end))
                    end
                else
                    -- 弹出开礼物箱子界面
                    local item_name = skin_ids_to_codes(to_add)
                    local options = {
                        allow_cancel = true,
                        box_build = "box_shop_plain",
                        message = "请收下你的礼包"
                    }
                    TheFrontEnd:PushScreen(ITEMBOXOPENERPOPUP(options, function(success_cb) success_cb(item_name) end))
                end
                return
            end
        end
    end
    TheFrontEnd:PushScreen(SmallPopup())
end

function SWShopPopup:OnControl(control, down)
    if SWShopPopup._base.OnControl(self, control, down) then return true end
    if not down and control == CONTROL_CANCEL then
        TheFrontEnd:PopScreen(self)
        return true
    end
end

-------------------------------------------------------------------------------
-- 监听 ] 键，按下弹出弹窗
-------------------------------------------------------------------------------
GLOBAL.TheInput:AddKeyHandler(function(key, down)
    if down and key == GLOBAL.KEY_RIGHTBRACKET then
        if ThePlayer and TheFrontEnd then
            -- 防止多开
            if not TheFrontEnd:GetOpenScreenOfType("SWShopPopup") then
                TheFrontEnd:PushScreen(SWShopPopup())
            end
        end
    end
end)
